package site.wenneng.dev.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import site.wenneng.dev.common.enumeration.DicEnum;
import site.wenneng.dev.objs.bo.TaskRemoteBO;
import site.wenneng.dev.objs.dto.StoryTaskViewDTO;
import site.wenneng.dev.objs.bo.StoryRemoteBO;
import site.wenneng.dev.objs.dto.StoryVersionDTO;
import site.wenneng.dev.objs.query.StoryRemoteQuery;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;

@Slf4j
@Service
@AllArgsConstructor
public class StoryService extends BaseService {

    private SimpleBizService simpleBizService;
    private TaskService taskService;
    private DictionaryService dictionaryService;

    @SneakyThrows
    public List<StoryRemoteBO> queryStoryFromRemote(StoryRemoteQuery query) {
        String payload = processRequest("req/story.json.ftlh", query);
        Request request = new Request.Builder()
                .url("https://dev.hundsun.com/heppm/story/getStoryMenuListV3")
                .post(RequestBody.create(payload, MediaType.parse("application/json")))
                .build();
        ObjectMapper objectMapper = getObjectMapper();
        JsonNode jsonNode = objectMapper.readTree(invoke(request));
        JsonNode codeNode = jsonNode.get("code");
        if (codeNode == null || !codeNode.textValue().equals("200")) {
            log.error("请求失败：{}", jsonNode.toPrettyString());
            return Collections.emptyList();
        }
        return objectMapper.readValue(jsonNode.get("data").get("items").traverse(), new TypeReference<List<StoryRemoteBO>>() {
        });
    }

    public List<StoryRemoteBO> queryStoryByStoryLabel(List<Integer> labelList, List<String> designerPersonList) {
        StoryRemoteQuery query = StoryRemoteQuery.builder()
                .page(1).pageSize(600)
                .productList(simpleBizService.listAllProductIdList())
                .projectList(simpleBizService.listAllProjectIdList())
                .storyLabelList(labelList)
                .designerPersonList(designerPersonList)
                .build();
        return queryStoryFromRemote(query);
    }

    public List<StoryRemoteBO> queryStoryByProject(List<Integer> projectList, List<String> designerPersonList) {
        StoryRemoteQuery query = StoryRemoteQuery.builder()
                .page(1).pageSize(600)
                .productList(simpleBizService.listAllProductIdList())
                .projectList(projectList)
                .designerPersonList(designerPersonList)
                .build();
        return queryStoryFromRemote(query);
    }

    public List<StoryRemoteBO> queryStoryByNumbers(Collection<String> storyNumbers) {
        if (CollectionUtils.isEmpty(storyNumbers)) {
            return Collections.emptyList();
        }
        StoryRemoteQuery query = StoryRemoteQuery.builder()
                .page(1).pageSize(600)
                .productList(simpleBizService.listAllProductIdList())
                .projectList(simpleBizService.listAllProjectIdList())
                .storyNumberList(new ArrayList<>(storyNumbers))
                .build();
        return queryStoryFromRemote(query);
    }

    public List<StoryTaskViewDTO> buildViewDTO(List<StoryRemoteBO> storyList, List<TaskRemoteBO> taskListIn) {
        List<TaskRemoteBO> taskList = taskListIn.stream().sorted(Comparator.comparing(TaskRemoteBO::getEstimateFinishTime)
                        .thenComparing(item -> item.getEstimateStartTime() == null ? new Date() : item.getEstimateStartTime())
                        .thenComparing(TaskRemoteBO::getEstimateWorkload))
                .collect(Collectors.toList());

        // 需求任务关系
        Map<String, List<TaskRemoteBO>> storyTaskMap = taskService.getStoryTaskMap(taskList, false);

        if (storyTaskMap.containsKey("202401010000")) {
            StoryRemoteBO storyRemoteBO = new StoryRemoteBO();
            storyRemoteBO.setId("202401010000");
            storyRemoteBO.setNumber("202401010000");
            storyRemoteBO.setName("日常事务");
            storyList.add(storyRemoteBO);
        }

        List<StoryTaskViewDTO> storyViewList = buildViewDTOWithStory(storyList);

        for (StoryTaskViewDTO story : storyViewList) {
            List<TaskRemoteBO> taskBOList = storyTaskMap.get(story.getNumber());
            story.setChildren(buildViewDTOWithTask(taskBOList));
        }

        fillFieldFromChildren(storyViewList);
        return storyViewList.stream()
                .filter(item -> !CollectionUtils.isEmpty(item.getChildren()))
                .sorted(Comparator.comparing(StoryTaskViewDTO::getEstimateFinishTime))
                .collect(Collectors.toList());
    }


    private List<StoryTaskViewDTO> buildViewDTOWithTask(List<TaskRemoteBO> taskList) {
        if (CollectionUtils.isEmpty(taskList)) {
            return Collections.emptyList();
        }
        return taskList.stream().map(item -> {
            StoryTaskViewDTO viewDTO = new StoryTaskViewDTO();
            viewDTO.setType("task");
            viewDTO.setKey(UUID.randomUUID().toString());
            viewDTO.setId(item.getId().toString());
            viewDTO.setNumber(item.getTaskNumber());
            viewDTO.setTitle(item.getName());
            viewDTO.setStatusName(item.getSubStatusName());
            viewDTO.setEstimateStartTime(item.getEstimateStartTime());
            viewDTO.setEstimateFinishTime(item.getEstimateFinishTime());
            viewDTO.setRemainDays(diff(toLocalDate(viewDTO.getEstimateFinishTime()), LocalDate.now()));
            viewDTO.setAssigneeName(item.getAssigneeName());
            viewDTO.setTaskType(item.getTaskType());
            viewDTO.setPriority(item.getPriority());
            viewDTO.setProjectId(item.getProjectId());
            viewDTO.setProductId(item.getProductId());
            viewDTO.setVersion(item.getSprintVersion());
            return viewDTO;
        }).collect(Collectors.toList());
    }

    private List<StoryTaskViewDTO> buildViewDTOWithStory(List<StoryRemoteBO> storyList) {
        if (CollectionUtils.isEmpty(storyList)) {
            return Collections.emptyList();
        }
        return storyList.stream().map(item -> {
            StoryTaskViewDTO viewDTO = new StoryTaskViewDTO();
            viewDTO.setType("story");
            viewDTO.setKey(UUID.randomUUID().toString());
            viewDTO.setId(item.getId());
            viewDTO.setNumber(item.getNumber());
            viewDTO.setTitle(item.getName());
            return viewDTO;
        }).collect(Collectors.toList());
    }

    private void fillFieldFromChildren(List<StoryTaskViewDTO> storyList) {
        Set<String> finishedTaskStatusSet = dictionaryService.listLabelSet(DicEnum.FINISHED_TASK_STATUS);
        for (StoryTaskViewDTO viewDTO : storyList) {
            List<StoryTaskViewDTO> children = viewDTO.getChildren();
            if (CollectionUtils.isEmpty(children)) {
                continue;
            }

            viewDTO.setStoryVersionList(calcStoryVersionList(finishedTaskStatusSet, children));
            viewDTO.setEstimateStartTime(children.stream().map(StoryTaskViewDTO::getEstimateStartTime).filter(Objects::nonNull).min(Comparator.comparing(item -> item)).orElse(null));
            viewDTO.setEstimateFinishTime(children.stream().map(StoryTaskViewDTO::getEstimateFinishTime).filter(Objects::nonNull).min(Comparator.comparing(item -> item)).orElse(null));
            viewDTO.setRemainDays(diff(toLocalDate(viewDTO.getEstimateFinishTime()), LocalDate.now()));
            viewDTO.setAssigneeName(children.stream().map(StoryTaskViewDTO::getAssigneeName).distinct().collect(Collectors.joining(",")));
            viewDTO.setStatusName(calcStoryStatus(children, finishedTaskStatusSet));
            viewDTO.setPriority(children.stream().map(StoryTaskViewDTO::getPriority).max(Comparator.naturalOrder()).orElse(4));
            viewDTO.setVersion(children.stream().map(StoryTaskViewDTO::getVersion).filter(Objects::nonNull).sorted().findFirst().orElse(""));
        }
    }

    @NotNull
    private List<StoryVersionDTO> calcStoryVersionList(Set<String> finishedTaskStatusSet, List<StoryTaskViewDTO> children) {
        Map<String, List<StoryTaskViewDTO>> map = children.stream()
                .filter(item -> item.getVersion() != null)
                .collect(Collectors.groupingBy(StoryTaskViewDTO::getVersion));
        return map.entrySet().stream()
                .map(item -> new StoryVersionDTO(item.getKey(), calcStoryStatus(item.getValue(), finishedTaskStatusSet)))
                .collect(toList());
    }

    private String calcStoryStatus(List<StoryTaskViewDTO> taskList, Set<String> finishedTaskStatusSet) {
        if (CollectionUtils.isEmpty(taskList)) {
            return "待分解";
        }

        List<StoryTaskViewDTO> devTask = taskList.stream()
                .filter(item -> item.getTaskType() != 103)
                .collect(toList());
        List<StoryTaskViewDTO> unFinishedTask = taskList.stream()
                .filter(item -> !finishedTaskStatusSet.contains(item.getStatusName()))
                .collect(toList());
        Set<StoryTaskViewDTO> unFinishedDesignTask = unFinishedTask.stream()
                .filter(item -> item.getTaskType() == 103)
                .collect(toSet());
        if (unFinishedDesignTask.size() != 0 || devTask.size() == 0) {
            return "设计中";
        }
        if (unFinishedTask.size() != 0) {
            boolean isTesting = unFinishedTask.stream().allMatch(item -> "待审核".equals(item.getStatusName()));
            if (isTesting) {
                return "联测中";
            } else if (taskList.stream().map(StoryTaskViewDTO::getStatusName).allMatch(item -> item.equals("待启动"))) {
                return "待启动";
            }
            return "开发中";

        }
        return "已完成";
    }

    private int diff(LocalDate date1, LocalDate date2) {
        return (int) ChronoUnit.DAYS.between(date2, date1);
    }

    private LocalDate toLocalDate(Date date) {
        if (date == null) {
            return LocalDate.now();
        }
        return date.toInstant().atZone(ZoneId.of("+0")).toLocalDate();
    }

}
